import Cesium from '../Ces/Cesium'
import VoxelRenderResources from './VoxelRenderResources'
import processVoxelProperties from "./processVoxelProperties.js";

/**
 * @function
 *
 * @param {VoxelPrimitive} primitive
 * @param {Context} context
 *
 * @private
 */
function buildVoxelDrawCommands(primitive, context) {
  const renderResources = new VoxelRenderResources(primitive);

  processVoxelProperties(renderResources, primitive);

  const {
    shaderBuilder,
    clippingPlanes,
    clippingPlanesLength,
  } = renderResources;

  if (clippingPlanesLength > 0) {
    // Extract the getClippingPlane function from the Cesium.getClippingFunction string.
    // This is a bit of a hack.
    const functionId = "getClippingPlane";
    const entireFunction = Cesium.getClippingFunction(clippingPlanes, context);
    const functionSignatureBegin = 0;
    const functionSignatureEnd = entireFunction.indexOf(")") + 1;
    const functionBodyBegin =
      entireFunction.indexOf("{", functionSignatureEnd) + 1;
    const functionBodyEnd = entireFunction.indexOf("}", functionBodyBegin);
    const functionSignature = entireFunction.slice(
      functionSignatureBegin,
      functionSignatureEnd
    );
    const functionBody = entireFunction.slice(
      functionBodyBegin,
      functionBodyEnd
    );
    shaderBuilder.addFunction(
      functionId,
      functionSignature,
      Cesium.ShaderDestination.FRAGMENT
    );
    shaderBuilder.addFunctionLines(functionId, [functionBody]);
  }

  // Compile shaders
  const shaderBuilderPick = shaderBuilder.clone();
  shaderBuilderPick.addDefine("PICKING", undefined, Cesium.ShaderDestination.FRAGMENT);
  const shaderBuilderPickVoxel = shaderBuilder.clone();
  shaderBuilderPickVoxel.addDefine(
    "PICKING_VOXEL",
    undefined,
    Cesium.ShaderDestination.FRAGMENT
  );
  const shaderProgram = shaderBuilder.buildShaderProgram(context);
  const shaderProgramPick = shaderBuilderPick.buildShaderProgram(context);
  const shaderProgramPickVoxel = shaderBuilderPickVoxel.buildShaderProgram(
    context
  );

  const depthTest = primitive._depthTest;
  const renderState = Cesium.RenderState.fromCache({
    cull: {
      enabled: true,
      face: Cesium.CullFace.BACK,
    },
    depthTest: {
      enabled: depthTest,
    },
    depthMask: depthTest,
    // internally the shader does premultiplied alpha, so it makes sense to blend that way too
    blending: Cesium.BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
  });

  // Create the draw commands
  const viewportQuadVertexArray = context.getViewportQuadVertexArray();
  const drawCommand = new Cesium.DrawCommand({
    vertexArray: viewportQuadVertexArray,
    primitiveType: Cesium.PrimitiveType.TRIANGLES,
    renderState: renderState,
    shaderProgram: shaderProgram,
    uniformMap: renderResources.uniformMap,
    modelMatrix: primitive._compoundModelMatrix,
    pass: Cesium.Pass.VOXELS,
    // executeInClosestFrustum: true,
    owner: this,
    cull: depthTest, // don't cull or occlude if depth testing is off
    occlude: depthTest, // don't cull or occlude if depth testing is off
  });

  // Create the pick draw command
  const drawCommandPick = Cesium.DrawCommand.shallowClone(
    drawCommand,
    new Cesium.DrawCommand()
  );
  drawCommandPick.shaderProgram = shaderProgramPick;
  drawCommandPick.pickOnly = true;

  // Create the pick voxels draw command
  const drawCommandPickVoxel = Cesium.DrawCommand.shallowClone(
    drawCommand,
    new Cesium.DrawCommand()
  );
  drawCommandPickVoxel.shaderProgram = shaderProgramPickVoxel;
  drawCommandPickVoxel.pickOnly = true;

  // Delete the old shader programs
  if (Cesium.defined(primitive._drawCommand)) {
    const command = primitive._drawCommand;
    command.shaderProgram =
      command.shaderProgram && command.shaderProgram.destroy();
  }
  if (Cesium.defined(primitive._drawCommandPick)) {
    const command = primitive._drawCommandPick;
    command.shaderProgram =
      command.shaderProgram && command.shaderProgram.destroy();
  }
  if (Cesium.defined(primitive._drawCommandPickVoxel)) {
    const command = primitive._drawCommandPickVoxel;
    command.shaderProgram =
      command.shaderProgram && command.shaderProgram.destroy();
  }

  primitive._drawCommand = drawCommand;
  primitive._drawCommandPick = drawCommandPick;
  primitive._drawCommandPickVoxel = drawCommandPickVoxel;
}

export default buildVoxelDrawCommands;
